﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static MyGIS.GISShapefile;

namespace MyGIS
{

    /// <summary>
    /// 创建自定义的空间数据格式
    /// </summary>
    public class GISMyFile
    {
        /// <summary>
        /// 文件头
        /// </summary>
        [StructLayout(LayoutKind.Sequential,Pack=4)]
        struct MyFileHeader
        {
            public double MinX, MinY, MaxX, MaxY;//地图范围
            public int FeatureCount, ShapeType, FieldCount;//对象数量，对象类型，属性字段个数
        }


        /// <summary>
        /// 将自定义的MyFileHeader的实例写入文件
        /// </summary>
        /// <param name="layer"></param>
        /// <param name="bw"></param>
        static void WriteFileHeader(GISLayer layer,BinaryWriter bw)
        {
            MyFileHeader mfh = new MyFileHeader();//创建MyFileHeader的实例
            mfh.MinX = layer.Extent.getMinX();
            mfh.MinY = layer.Extent.getMinY();
            mfh.MaxX = layer.Extent.getMaxX();
            mfh.MaxY = layer.Extent.getMaxY();
            mfh.FeatureCount = layer.FeatureCount();
            mfh.ShapeType = (int)(layer.ShapeType);
            mfh.FieldCount = layer.Fields.Count;
            bw.Write(GISTools.ToBytes(mfh));//bw是与某个文件相连的文件写入工具，在文件打开时会得到
        }


        /// <summary>
        /// 写自定义格式的文件
        /// </summary>
        /// <param name="layer"></param>
        /// <param name="filename"></param>
        public static void WriteFile(GISLayer layer,string filename)
        {
            FileStream fsr = new FileStream(filename, FileMode.Create);//根据文件名创建一个新文件
            BinaryWriter bw = new BinaryWriter(fsr);
            WriteFileHeader(layer, bw);//写入文件头
            GISTools.WriteString(layer.Name, bw);
            WriteField(layer.Fields, bw);
            WriteFeatures(layer, bw);
            bw.Close();
            fsr.Close();
        }


        /// <summary>
        /// 输出所有字段信息到文件
        /// </summary>
        /// <param name="fields"></param>
        /// <param name="bw"></param>
        static void WriteField(List<GISField>fields,BinaryWriter bw)
        {
            for (int fieldindex = 0; fieldindex < fields.Count; fieldindex++)
            {
                GISField field = fields[fieldindex];
                bw.Write(GISTools.TypeToInt(field.datatype));
                GISTools.WriteString(field.name, bw);
            }
        }

        /// <summary>
        /// 将多个节点坐标输出到二进制文件
        /// </summary>
        /// <param name="vs"></param>
        /// <param name="bw"></param>
        static void WriteMultipleVertexs(List<GISVertex>vs,BinaryWriter bw)
        {
            bw.Write(vs.Count);
            for (int vc = 0; vc < vs.Count; vc++)
            {
                vs[vc].WriteVertex(bw);
            }
        }


        /// <summary>
        /// 为自定义GIS格式写入属性数据
        /// </summary>
        /// <param name="attribute"></param>
        /// <param name="bw"></param>
        static void WriteAttributes(GISAttribute attribute,BinaryWriter bw)
        {
            for (int i = 0; i < attribute.ValueCount(); i++)
            {
                Type type = attribute.GetValue(i).GetType();
                if (type.ToString()=="System.Boolean")
                {
                    bw.Write((bool)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Byte")
                {
                    bw.Write((byte)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Char")
                {
                    bw.Write((char)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Decimal")
                {
                    bw.Write((decimal)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Double")
                {
                    bw.Write((double)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Single")
                {
                    bw.Write((float)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Int16")
                {
                    bw.Write((short)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Int32")
                {
                    bw.Write((int)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.Int64")
                {
                    bw.Write((long)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.UInt16")
                {
                    bw.Write((ushort)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.UInt32")
                {
                    bw.Write((uint)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.UInt64")
                {
                    bw.Write((ulong)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.SByte")
                {
                    bw.Write((sbyte)attribute.GetValue(i));
                }
                else if (type.ToString() == "System.String")
                {
                    GISTools.WriteString((string)attribute.GetValue(i), bw);
                }
            }
        }



        /// <summary>
        /// 输出自定义GIS格式一个图层中所有的空间要素
        /// </summary>
        /// <param name="layer"></param>
        /// <param name="bw"></param>
        static void WriteFeatures(GISLayer layer,BinaryWriter bw)
        {
            for (int featureindex = 0; featureindex < layer.FeatureCount(); featureindex++)
            {
                GISFeature feature = layer.GetFeature(featureindex);
                if (layer.ShapeType == SHAPETYPE.point)
                {
                    ((GISPoint)feature.spatialpart).centroid.WriteVertex(bw);
                }
                else if (layer.ShapeType == SHAPETYPE.line)
                {
                    GISLine line = (GISLine)(feature.spatialpart);
                    WriteMultipleVertexs(line.Vertexs, bw);
                }
                else if (layer.ShapeType == SHAPETYPE.poloygon)
                {
                    GISPolygon polygon = (GISPolygon)(feature.spatialpart);
                    WriteMultipleVertexs(polygon.Vertexs, bw);
                }
                WriteAttributes(feature.attributepart, bw);
            }
        }


        /// <summary>
        /// 从自定义的GIS格式文件中读取字段信息
        /// </summary>
        /// <param name="br"></param>
        /// <param name="FieldCount"></param>
        /// <returns></returns>
        static List<GISField>ReadFields(BinaryReader br,int FieldCount)
        {
            List<GISField> fields = new List<GISField>();
            for (int fieldindex = 0; fieldindex < FieldCount; fieldindex++)
            {
                Type fieldtype = GISTools.IntToType(br.ReadInt32());
                string fieldname = GISTools.ReadString(br);
                fields.Add(new GISField(fieldtype, fieldname));
            }
            return fields;
        }


        /// <summary>
        /// 连续读取多个GISVertex实例
        /// </summary>
        /// <param name="br"></param>
        /// <returns></returns>
        static List<GISVertex>ReadMultipleVertexes(BinaryReader br)
        {
            List<GISVertex> vs = new List<GISVertex>();
            int vcount = br.ReadInt32();
            for (int vc = 0; vc < vcount; vc++)
            {
                vs.Add(new GISVertex(br));
            }
            return vs;
        }


        /// <summary>
        /// 读取一个GISFeature的所有属性值
        /// </summary>
        /// <param name="fs"></param>
        /// <param name="br"></param>
        /// <returns></returns>
        static GISAttribute ReadAttrubute(List<GISField>fs,BinaryReader br)
        {
            GISAttribute attribute = new GISAttribute();
            for (int i = 0; i < fs.Count; i++)
            {
                Type type = fs[i].datatype;
                if (type.ToString() == "System.Boolean")
                {
                    attribute.AddValue(br.ReadBoolean());
                }
                else if (type.ToString() == "System.Byte")
                {
                    attribute.AddValue(br.ReadByte());
                }
                else if (type.ToString() == "System.Char")
                {
                    attribute.AddValue(br.ReadChar());
                }
                else if (type.ToString() == "System.Decimal")
                {
                    attribute.AddValue(br.ReadDecimal());
                }
                else if (type.ToString() == "System.Double")
                {
                    attribute.AddValue(br.ReadDouble());
                }
                else if (type.ToString() == "System.Single")
                {
                    attribute.AddValue(br.ReadSingle());
                }
                else if (type.ToString() == "System.Int16")
                {
                    attribute.AddValue(br.ReadInt16());
                }
                else if (type.ToString() == "System.Int32")
                {
                    attribute.AddValue(br.ReadInt32());
                }
                else if (type.ToString() == "System.Int64")
                {
                    attribute.AddValue(br.ReadInt64());
                }
                else if (type.ToString() == "System.UInt16")
                {
                    attribute.AddValue(br.ReadUInt16());
                }
                else if (type.ToString() == "System.UInt32")
                {
                    attribute.AddValue(br.ReadUInt32());
                }
                else if (type.ToString() == "System.UInt64")
                {
                    attribute.AddValue(br.ReadUInt64());
                }
                else if (type.ToString() == "System.SByte")
                {
                    attribute.AddValue(br.ReadSByte());
                }
                else if (type.ToString() == "System.String")
                {
                    attribute.AddValue(br.ReadString());
                }
            }
            return attribute;
        }


        /// <summary>
        /// 读文件中所有的GISFeature的空间及属性值
        /// </summary>
        /// <param name="layer"></param>
        /// <param name="br"></param>
        /// <param name="FeatureCount"></param>
        static void ReadFeatures(GISLayer layer,BinaryReader br,int FeatureCount)
        {
            for (int featureindex = 0; featureindex < FeatureCount; featureindex++)
            {
                GISFeature feature = new GISFeature(null, null);
                if (layer.ShapeType == SHAPETYPE.point)
                {
                    feature.spatialpart = new GISPoint(new GISVertex(br));
                }
                else if (layer.ShapeType == SHAPETYPE.line)
                {
                    feature.spatialpart = new GISLine(ReadMultipleVertexes(br));
                }
                else if (layer.ShapeType == SHAPETYPE.poloygon)
                {
                    feature.spatialpart = new GISPolygon(ReadMultipleVertexes(br));
                }
                feature.attributepart = ReadAttrubute(layer.Fields, br);
                layer.AddFeature(feature);
            }
        }


        /// <summary>
        /// 读取自定义格式的文件，返回值为一个图层
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static GISLayer ReadFile(string filename)
        {
            FileStream fsr = new FileStream(filename, FileMode.Open);//打开文件
            BinaryReader br = new BinaryReader(fsr);
            MyFileHeader mfh = (MyFileHeader)(GISTools.FromBytes(br, typeof(MyFileHeader)));//读取文件头
            SHAPETYPE ShapeType = (SHAPETYPE)Enum.Parse(typeof(SHAPETYPE), mfh.ShapeType.ToString());
            GISExtent extent = new GISExtent(new GISVertex(mfh.MinX, mfh.MinY), new GISVertex(mfh.MaxX, mfh.MaxY));
            string layername = GISTools.ReadString(br);
            List<GISField> fields = ReadFields(br, mfh.FieldCount);//读取字段信息
            GISLayer layer = new GISLayer(layername, ShapeType, extent,fields);//新建图层
            ReadFeatures(layer, br, mfh.FeatureCount);
            br.Close();
            fsr.Close();
            return layer;
        }
    }
}
